package admin.gateway.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.JWSObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import admin.common.enums.ResponseEnum;
import admin.common.constant.AuthConstant;
import admin.common.domain.rsp.R;

import java.text.ParseException;
import java.util.Optional;

/**
 * @author zgb
 * @date 2024-03-07 19:10
 * @description TODO
 */
@Component
public class TokenBlackListGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {

    @Autowired
    private ReactiveRedisTemplate<String, Object> reactiveRedisTemplate;


    @Value("${auth.sessionYn}")
    private Boolean sessionYn;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            if(sessionYn){
                return chain.filter(exchange);
            }
            ServerHttpRequest request = exchange.getRequest();
            String token = request.getHeaders().getFirst(AuthConstant.JWT_TOKEN_HEADER);
            if (StrUtil.isNotEmpty(token)) {
                String realToken = token.replace(AuthConstant.JWT_TOKEN_PREFIX, "");
                JWSObject jwsObject = null;
                try {
                    jwsObject = JWSObject.parse(realToken);
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
                String payload = jwsObject.getPayload().toString();
                JSONObject jsonObject = JSONUtil.parseObj(payload);
                // JWT唯一标识
                String jti = jsonObject.getStr(AuthConstant.JTI_KEY);
                return this.getJti(jti).flatMap(op -> {
                    //1、判断jti中是否存在对应缓存
                    if (op.isPresent()) {
                        ServerHttpResponse response = exchange.getResponse();
                        return handlerNoPass(response);
                    }
                    //3、token鉴权通过，继续执行filter完成转发
                    return chain.filter(exchange);
                });
            }
            return chain.filter(exchange);
        };
    }

    private Mono<Optional<String>> getJti(String jti) {
        //a、以reactive的方式查询redis，获取token对应value值
        return this.reactiveRedisTemplate.opsForValue().get(AuthConstant.TOKEN_BLACKLIST_PREFIX +jti)
                //b、对redis返回结果进行Optional封装，这里v为String类型的用户id
                .map(v -> Optional.ofNullable((String)v))
                //c、如果token无效，b不会执行，在这里返回1个值为null的Optional对象
                .switchIfEmpty(Mono.just(Optional.ofNullable(null)));
    }

    private Mono<Void> handlerNoPass(ServerHttpResponse response) {
        R r = new R();
        r.setCode(ResponseEnum.UNAUTHORIZED.getCode());
        r.setMsg(ResponseEnum.UNAUTHORIZED.getMsg());
        //响应
        //  Mono<Void> writeWith(Publisher<? extends DataBuffer> var1);
        return response.writeWith(
                Mono.just(response.bufferFactory().wrap(
                                //DataBuffer
                                JSONUtil.toJsonStr(r).getBytes()
                        )
                )

        );
    }
}